/*==========================================================================
 * Copyright (c) 2001 Carnegie Mellon University.  All Rights Reserved.
 *
 * Use of the Lemur Toolkit for Language Modeling and Information Retrieval
 * is subject to the terms of the software license set forth in the LICENSE
 * file included with this software, and also available at
 * http://www.lemurproject.org/license.html
 *
 *==========================================================================
 */

#ifndef _EXCEPTION_H
#define _EXCEPTION_H
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

namespace MyUtil 
{
    /// Error codes.
    typedef unsigned long LemurErrorType; 
    /// Default Exception class
    /*!
      This is the base Exception class that all other exceptions should
      inherit from. 
      Some guidelines for using Exception:


      (1) Define your main function as "int AppMain(int argc, char *argv[])"
      rather than the normal "main" function. 
      (2) Use the KAKA_THROW and KAKA_RETHROW macros to throw the exception, and
      to pass it along to the next level's handler, respectively.
      To use KAKA_THROW, include the appropriate Lemur error code, and whatever
      string description you want.   File and line information are automatically
      included.
      The error code can be from the standard Lemur list (see below).
      (3)  Here's an example of how you might use KAKA_THROW to generate an exception,
      and KAKA_RETHROW to pass it along to the next handler.

      <PRE> 
      try { 
      ... ... 
    // within the index open routine, this exception might be thrown:
    KAKA_THROW(KAKA_IO_ERROR, "The index file does not exist");
    }
    catch  (Exception &ex) {
    KAKA_RETHROW(ex, "Could not start retrieval program.");
    }

    </PRE>
    In general, consistent use of KAKA_THROW and KAKA_RETHROW will result in a 
    nested series of exception messages, showing successively lower
    levels of exception information, allowing easy tracing of the failure path.

    (4) If the exception is not caught in the application, it will be
    caught be the main function in the lemur toolkit. The default exception handler prints the following
    message on stderr and terminates the program. 
    <PRE>
    Exception FileName.cpp(#linenum): The index file does not exist
    Program aborted due to exception
    </PRE>

    (5) You can define more specific exceptions by subclassing Exception.
    All exceptions will be caught by the default main function if not caught by an application.


*/

    class Exception {
	public:
	    Exception(char *throwerString=" unknown thrower", char *whatString="unknown exception") {
		_what = throwerString;
		_what += ": ";
		_what += whatString;
	    }

	    Exception( const std::string& whoString, int whereLine, 
		    const std::string& whatString, LemurErrorType code )
	    {
		std::stringstream lineString;
		lineString << whereLine;

		_what = whoString + "(" + lineString.str() + ")" + ": " + whatString;
		_code = code;
	    }

	    Exception( const std::string& whoString, int whereLine,
		    const std::string& whatString, const Exception& inner )
	    {
		std::stringstream lineString;
		lineString << whereLine;

		_what = whoString + "(" + lineString.str() + "): " + whatString + "\n\t" + inner.what();
		_code = inner.code();
	    }

	    ~Exception() {}

	    inline void writeMessage(std::ostream &os = std::cerr)
	    {
		os << "Exception [code = " << _code << "]" << std::endl << _what << std::endl;
	    }

	    const std::string& what() const {
		return _what;
	    }

	    LemurErrorType code() const {
		return _code;
	    }

	private:
	    std::string _what;
	    LemurErrorType _code;
    };
}

#define KAKA_ABORT( e )                  { std::cerr << e.what() << std::endl; exit(-1); }
#define KAKA_THROW_LINE( code, text, file, line )  throw MyUtil::Exception( file, line, std::string() + text, (code) )
#define KAKA_THROW(code, text)  KAKA_THROW_LINE(code, text, __FILE__, __LINE__)
#define KAKA_RETHROW_LINE( e, text, file, line )   throw MyUtil::Exception( file, line, (std::string() + text), (e) )
#define KAKA_RETHROW( e, text)  KAKA_RETHROW_LINE(e, text, __FILE__, __LINE__)

#define KAKA_GENERIC_ERROR               ((MyUtil::LemurErrorType)0xFFFFFFFF)
#define KAKA_MISSING_PARAMETER_ERROR     ((MyUtil::LemurErrorType)0xFFFFFFFE)
#define KAKA_BAD_PARAMETER_ERROR         ((MyUtil::LemurErrorType)0xFFFFFFF7)
#define KAKA_PARSE_ERROR                 ((MyUtil::LemurErrorType)0xFFFFFFFD)
#define KAKA_KEYFILE_IO_ERROR            ((MyUtil::LemurErrorType)0xFFFFFFFC)
#define KAKA_IO_ERROR                    ((MyUtil::LemurErrorType)0xFFFFFFFB)
#define KAKA_RUNTIME_ERROR               ((MyUtil::LemurErrorType)0xFFFFFFFA)
#define KAKA_NETWORK_ERROR               ((MyUtil::LemurErrorType)0xFFFFFFF9)
#define KAKA_INTERNAL_ERROR              ((MyUtil::LemurErrorType)0xFFFFFFF8)

#endif
